<?php
class Tag_Parser{
	/**
	 * 源代码
	 *
	 * @var string
	 */
	var $source;
	static $_class = __CLASS__;
	/**
	 * Namespace
	 *
	 */
	const NS = "i";
	/**
	 * tag开头
	 *
	 */
	const TAG_START = "<i:";
	/**
	 * XSL HEADER
	 *
	 */
	const XSL_HEADER = '<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:i="178"><xsl:output method="html" omit-xml-declaration="no" disable-output-escaping="yes" standalone="yes" encoding="UTF-8" indent="yes"/>';
	/**
	 * XSL FOOTER
	 *
	 */
	const XSL_FOOTER = '</xsl:stylesheet>';
	/**
	 * 支持的tag 映射
	 *
	 * @var []
	 */
	static $tags = array(
		'button'=>'Tag_Button',
		'app'=>'Tag_App',
		'tab-nav'=>'Tag_Tabnav',
		'literal-nav'=>'Tag_Literalnav',
		'avatar'=>'Tag_Avatar',
		'money'=>'Tag_Money',
		'pages'=>'Tag_Page',
		'right-nav'=>'Tag_Rightnav',
		'user-table'=>'Tag_Usertable',
		'editor'=>'Tag_Editor',
		'comment'=>'Tag_Comment',
		'name'=>'Tag_Name',
		'user-nav'=>'Tag_Usernav',
		'operation-nav'=>'Tag_Operationnav',
		'combo'=>'Tag_Combo',
		'friend-list-box'=>'Tag_Friendlist',
		'info'=>'Tag_Info',
		'exp'=>'Tag_Exp',
		'loading'=>'Tag_Loading',
		'online' => 'Tag_Online'
	);
	/**
	 * 
	 *
	 * @var DOMDocument
	 */
	var $xsl;
	/**
	 * 
	 *
	 * @var DOMDocument
	 */
	var $xml;
	/**
	 * 
	 *
	 * @var XSLTProcessor
	 */
	var $processor;
	
	public function __construct($source){
		$this->source = $source;
		$this->xsl = new DOMDocument();
		$this->xml = new DOMDocument();
		$this->processor = new XSLTProcessor();
	}
	
	/**
	 * 从指定位置扫描文档
	 *
	 * @param int $pos
	 * @return string
	 */
	private function scan($pos=0){
		list($start, $end, $tag) = $this->fetchPiece($pos);
		if($start===false) return $this->source;
		$len = $this->transform($start, $end, $tag);
		if($len>=0){
			return $this->scan($start+$len);
		}else{
			return $this->scan($end);
		}
	}
	
	/**
	 * 搜索支持tag的开始位置
	 *
	 * @param int $pos
	 * @return [pos, tagname]
	 */
	private function findStart($pos){
		if($pos>=strlen($this->source)) return array(false, null);
		$occur = strpos($this->source, self::TAG_START, $pos);
		if($occur===false) return array(false, null);
		$flag = preg_match('`'.self::TAG_START.'([a-z0-9_\-]+)`i', substr($this->source, $occur), $match);
		if(!$flag) {
			throw new Tag_Exception(
				Tag_Exception::TXT_WRONG_FORMAT ,
				Tag_Exception::CODE_WRONG_FORMAT 
			);
		}
		return array($occur, $match[1]);
	}
	
	/**
	 * 搜索tag的结束位置
	 *
	 * @param int $pos
	 * @param string $tag
	 * @return int
	 */
	private function findEnd($pos, $tag){
//		$buffer = '';
		$char = null;
		$pre_char = null;
		$tag_start = false;
		$tag_body_end = false;
		$tag_end = false;
		$len = strlen($this->source);
		while(1){
			$char = $this->source[$pos];
			if(!$tag_start && $char=="<"){
				$tag_start = true;
			}
			if(!$tag_body_end && $char==">" && $pre_char=="/"){
				$tag_body_end = true;
				$tag_end = true;
			}
			if(!$tag_body_end && $char==">"){
				$tag_body_end = true;
			}
			if($tag_body_end && !$tag_end 
				&& $char=="/" && $pre_char=="<" 
				&& strtoupper(substr($this->source, $pos+1, strlen(self::NS.":".$tag))) == strtoupper(self::NS.":".$tag) 
				&& substr($this->source, $pos+strlen(self::NS.":".$tag)+1,1)==">"
				){
				$p = strpos($this->source, ">", $pos);
				if($p!==false){
					$pos = $p;
					$tag_end = true;
				}
			}
			$pre_char = $char;
			$pos++;
			if($tag_end || $pos>=$len){
				break;
			}
		}
		
		if($tag_end){
			return $pos;
		}else{
			throw new Tag_Exception(
				Tag_Exception::TXT_NO_ENDING_TAG ,
				Tag_Exception::CODE_NO_ENDING_TAG 
			);
		}
	}
	
	/**
	 * 截取片段
	 *
	 * @param int $pos
	 * @return [start, end, tagname]
	 */
	private function fetchPiece($pos){
		list($start, $tag) = $this->findStart($pos);
		if($start===false) return array(false, false, null);
		$end = $this->findEnd($start, $tag);
		return array($start, $end, $tag);
	}
	
	/**
	 * 使用转换后的文档替换源文件中的相应代码片段
	 *
	 * @param int $start
	 * @param int $end
	 * @param string $transform_text
	 */
	private function replace($start, $end, $transform_text){
		$this->source = substr($this->source, 0, $start).$transform_text.substr($this->source, $end);
	}
	
	/**
	 * 使用xsl转换文档
	 *
	 * @param int $start
	 * @param int $end
	 * @param string $tag
	 * @return int - 转换后的文档长度，如果是未支持的标记返回-1
	 */
	private function transform($start, $end, $tag){
//		if(key_exists($tag, $_SERVER['config']['translate'])){
		if(key_exists($tag, self::$tags )){
			$xsl = '';
			switch($tag){
				case "button":
					$xsl = Tag_Button::$XSL ;
					break;
				case "app":
					$xsl = Tag_App::$XSL ;
					break;
				case "tab-nav":
					$xsl = Tag_Tabnav::$XSL ;
					break;
				case "literal-nav":
					$xsl = Tag_Literalnav::$XSL ;
					break;
				case "avatar":
					$xsl = Tag_Avatar::$XSL ;
					break;
				case "money":
					$xsl = Tag_Money::$XSL ;
					break;
				case "pages":
					$xsl = Tag_Page::$XSL;
					break;
				case "right-nav":
					$xsl = Tag_Rightnav::$XSL;
					break;
				case "user-table":
					$xsl = Tag_Usertable::$XSL;
					break;
				case "editor":
					$xsl = Tag_Editor::$XSL ;
					break;
				case "comment":
					$xsl = Tag_Comment::$XSL ;
					break;
				case "name":
					$xsl = Tag_Name::$XSL ;
					break;
				case "user-nav":
					$xsl = Tag_Usernav::$XSL;
					break;
				case "operation-nav":
					$xsl = Tag_Operationnav::$XSL;
					break;
				case "combo":
					$xsl = Tag_Combo::$XSL;
					break;
				case "friend-list-box":
					$xsl = Tag_Friendlist::$XSL;
					break;
				case "info":
					$xsl = Tag_Info::$XSL;
					break;
				case "exp":
					$xsl = Tag_Exp::$XSL;
					break;
				case "online":
					$xsl = Tag_Online::$XSL;
					break;
			}
			$xml = substr($this->source, $start, $end-$start);
			$this->xml->loadXML($xml);
			$this->xsl->loadXML(self::XSL_HEADER . $xsl . self::XSL_FOOTER );
			$this->processor->importStylesheet($this->xsl);
			$result = $this->processor->transformToXml($this->xml);
			$this->replace($start, $end, $result);
			return strlen($result);
		}else{
			return -1;
		}
	}
	
	/**
	 * 翻译源代码
	 *
	 * @param  string $source
	 * @return string
	 */
	public static function translate($source){
		$parser = new self::$_class($source);
		return $parser->scan();
	}
}

//$_SERVER['config']['translate'] = array(
//	'dashboard'=>'
//<xsl:template match="/">
//<b><xsl:value-of select="/i:dashboard/@title"/></b>
//<xsl:apply-templates/>
//</xsl:template>	
//
//<xsl:template match="i:action">
//<a href="{@href}"><xsl:value-of select="."/></a>
//</xsl:template>
//	',
//	'button'=>'
//<xsl:variable name="noBtn" select="\'cbtn\'"/>
//<xsl:variable name="yesBtn" select="\'cbtn1\'"/>
//<xsl:variable name="unableBtn" select="\'cbtn1-unable\'"/>
//
//<xsl:template match="i:button">
//<xsl:choose>
//  <xsl:when test="@disabled=\'true\'">
//  <xsl:call-template name="button-style">
//    <xsl:with-param name="className" select="$unableBtn"/>
//  </xsl:call-template>
//  </xsl:when>
//  <xsl:when test="@style=\'yes\'">
//  <xsl:call-template name="button-style">
//    <xsl:with-param name="className" select="$yesBtn"/>
//  </xsl:call-template>
//  </xsl:when>
//  <xsl:when test="@style=\'no\'">
//  <xsl:call-template name="button-style">
//    <xsl:with-param name="className" select="$noBtn"/>
//  </xsl:call-template>
//  </xsl:when>
//</xsl:choose>
//</xsl:template>
//
//<xsl:template  name="button-style">
//<xsl:param name="className"/>
//<xsl:choose>
//<xsl:when test="@type=\'button\'">
//<button class="{$className}" id="{@id}" type="{@btype}" onclick="{@onclick}">
//	<xsl:if test="@disabled=\'true\'">
//		<xsl:attribute name="disabled"/>
//	</xsl:if>
//	<xsl:value-of select="@label"/>
//</button>
//</xsl:when>
//<xsl:when test="@type=\'link\'">
//<a class="{$className}" id="{@id}" onclick="{@onclick}" href="{@href}">
//	<xsl:if test="@disabled=\'true\'">
//		<xsl:attribute name="disabled"/>
//	</xsl:if>
//	<xsl:value-of select="@label"/>
//</a>
//</xsl:when>
//</xsl:choose>
//</xsl:template>'
//);
//
//$txt = '
//<i:dashboard xmlns:i="178" title="hello world">  
//<i:action href="new.php">创建新相册</i:action>  
//<i:action href="you.php">您的照片</i:action> 
//<i:create-button href="new.php">创建新相册</i:create-button> 
//<i:help href="help.php">帮助</i:help> 
//<p>123</p>
//</i:dashboard>
//';

// Button
//$txt2 = '<div><i:button xmlns:i="178" 
//   id="btn1" label="click" type="button" style="yes" btype="button" onclick="alert(1)" disabled="true">xxx</i:button>
//<i:button xmlns:i="178" 
//   id="btn2" label="me" type="button" style="no" btype="button" onclick="alert(1)"/>   
//<i:button xmlns:i="178" 
//   id="btn3" label="me" type="link" style="no" href="#" onclick="alert(1)"/>   </div>
//   ';

// App
//$txt2 = ' <i:app xmlns:i="178" icon="friends" title="好友">
//   <i:app-item title="1" url="http://1"/>
//   <i:app-item title="2" url="http://2"/>
//   <i:app-item title="3" url="http://3"/>
// </i:app>';

// tab-nav
//$txt2 = <<<EOD
// <i:tab-nav xmlns:i="178" id="tab" title="好友动态" dynamic='true'>
//   <i:tab-nav-item title="全部" url="javascript: Notify.load('all')" selected="true"/>
//   <i:tab-nav-item title="心情" url="javascript: Notify.load('record')"/>
//   <i:tab-nav-item title="相册" url="javascript: Notify.load('album')"/>
//   <i:tab-nav-more title="More" url="/?more"/>
// </i:tab-nav>
//EOD;

// literal-nav
//$txt2 = <<<EOD
// <i:literal-nav xmlns:i="178" delimiter="@">
//   <i:literal-nav-item title="1" url="link1"/>
//   <i:literal-nav-item title="2" url="link2" selected="true"/>
//   <i:literal-nav-item title="3" url="link3"/>
// </i:literal-nav>
//EOD;

//echo Tag_Parser::translate($txt2);
?>